#ifndef THREAD_UTIL_H
#define THREAD_UTIL_H
#include <Config.h>
#include <FastDelegate.h>
#include <utility>
#include <DateTime.h>
using namespace std;
using namespace fastdelegate;

typedef FastDelegate1<void*, void*> ThreadDelegate;

#ifdef WIN32
#include <winbase.h>
typedef HANDLE thread_t;

const thread_t NullThread = 0;

class Thread
{
public:
	static bool Create(ThreadDelegate func, void* param = NULL)
	{
		thread_t th;
		return Create(th, func, param);
	}
	static bool Create(volatile thread_t& th, FastDelegate1<void*, void*> func, void* param = NULL)
	{
		thread_start_info* info = new thread_start_info(func, param);
		DWORD tid = 0;
		th = ::CreateThread(NULL, 0, &Thread::ThreadStarter, (LPVOID)info, 0, &tid);
		return th != NULL;
	}
	static bool Kill(thread_t th)
	{
		return TerminateThread(th, 0) != FALSE;
	}
	static void Exit(void* exitCode)
	{
		::ExitThread((DWORD)exitCode);
	}
	// pesuado handle which can be used only in its own thread
	static thread_t Self()
	{
		return ::GetCurrentThread();
	}
	static void YieldSlice()
	{
		Sleep(0);
	}
	static void Sleep(int64_t microsec)
	{
		::Sleep((DWORD)microsec / 1000);
	}
private:
	static DWORD WINAPI ThreadStarter(void* lpParam)
	{
		thread_start_info* info = (thread_start_info*)lpParam;
		DWORD ret = (DWORD)(info->first(info->second));
		delete info;
		return ret;
	}
	typedef pair<ThreadDelegate, void*> thread_start_info;
};
#else

#include <pthread.h>
#include <unistd.h>

typedef pthread_t thread_t;

const thread_t NullThread = 0;

class Thread
{
public:
	static bool Create(FastDelegate1<void*, void*> func, void* param = NULL)
	{
		thread_t th;
		return Create(th, func, param);
	}
	static bool Create(volatile thread_t& th, FastDelegate1<void*, void*> func, void* param = NULL)
	{
		thread_start_info* info = new thread_start_info(func, param);
		pthread_attr_t attr;
		pthread_attr_init(&attr);
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
		bool ret = (pthread_create((thread_t*)&th, &attr, &Thread::ThreadStarter, info) == 0);
		pthread_attr_destroy(&attr);
		return ret;
	}
	static bool Kill(thread_t th)
	{
		return pthread_cancel(th) == 0;
	}
	static void Exit(void* exitCode)
	{
		pthread_exit(exitCode);
	}
	static thread_t Self()
	{
		return pthread_self();
	}
	static void YieldSlice()
	{
		pthread_yield();
	}
	static bool Sleep(int64_t microsec)
	{
		usleep(microsec);
	}
private:
	static void* ThreadStarter(void* lpParam)
	{
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
		pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
		thread_start_info* info = (thread_start_info*)lpParam;
		void* ret = info->first(info->second);
		delete info;
		return ret;
	}
	typedef pair<FastDelegate1<void*, void*>, void*> thread_start_info;
};
#endif

class SimpleEvent
{
public:
	SimpleEvent()
	{
		signaled = false;
	}
	bool WaitForSignal(int64_t timeout)
	{
		int64_t start = DateTime::Now().GetTickCount();
		while(!signaled)
		{
			int64_t diff = DateTime::Now().GetTickCount() - start;
			if(diff >= timeout)
				return false;
			Thread::YieldSlice();
		}
		return true;
	}
	bool WaitForUnsignal(int64_t timeout)
	{
		int64_t start = DateTime::Now().GetTickCount();
		while(signaled)
		{
			int64_t diff = DateTime::Now().GetTickCount() - start;
			if(diff >= timeout)
				return false;
			Thread::YieldSlice();
		}
		return true;
	}
	void Signal()
	{
		signaled = true;
	}
	void Reset()
	{
		signaled = false;
	}
private:
	volatile bool signaled;
};

#endif // !defined(THREAD_UTIL_H)
